package com.such.kit.io.source;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
import com.such.kit.Callable;
import com.such.kit.easyparam.ParamMap;
import com.such.kit.io.simple.support.IOSimple;
import com.such.kit.io.support.IOSource;
import com.such.kit.placeholder.PlaceholderCommons;
import com.such.kit.validate.ValidateCommons;

/**
 * @author SUCH
 * <pre>
 * [继承扩展于 {@link IOSource}] 以内存作为 IO 数据源的实现
 * 
 * 在 MemoryIOSource 中 inputExpression 与 outputExpression 的值一般是一致的，推荐值是一组数据的 key
 * 可以使用 {@link PlaceholderCommons 占位符} 引用数据、{@link IOSimple#args 自定义参数集} 构建动态 key
 * 动态 key 的灵活使用可以构建数据的树形结构，如 xml、json 这类易于构建树形结构的 IO 类型
 * 以 xml [举例] 构建动态 key
 * {@code
 * <?xml version="1.0" encoding="UTF-8"?>
 * <database
 *     dbType="#[dbType]" 
 *     <!-- database 在此例中必然只有一个，因此无需使用动态 key -->
 *     IO_INPUT_EXPRESSION="database" 
 *     IO_OUTPUT_EXPRESSION="database"
 * >
 *   <tables>
 *     <table 
 *         schema="#[schema]" 
 *         tableName="#[tableName]" 
 *         tableType="#[tableType]" 
 *         tableComment="#[tableComment]" 
 *         <!-- table 为业务意义上的 root key 可以不使用动态 key，方便直接获取 -->
 *         IO_INPUT_EXPRESSION="table" 
 *         IO_OUTPUT_EXPRESSION="table"
 *     >
 *       <columns>
 *         <column 
 *             schema="#[schema]" 
 *             tableName="#[tableName]" 
 *             columnName="#[columnName]" 
 *             dataType="#[dataType]" 
 *             columnSize="#[columnSize]" 
 *             decimalDigits="#[decimalDigits]" 
 *             defaultValue="#[defaultValue]" 
 *             notNull="#[notNull]" 
 *             autoincrement="#[autoincrement]" 
 *             columnComment="#[columnComment]" 
 *             <!--
 *               #[schema]_#[tableName]_columns 是一个动态 key，其含义是将一张表下所有列与表构建上下级关系
 *               #[schema]_#[tableName]_columns 会根据表的不同，动态构建出不同的值，如 IO_TABLE1_columns、IO_TABLE2_columns
 *               这样就可以通过 #[schema]_#[tableName]_columns 与表数据构建出正确的值，快速找到其下属的所有列
 *             -->
 *             IO_INPUT_EXPRESSION="#[schema]_#[tableName]_columns" 
 *             IO_OUTPUT_EXPRESSION="#[schema]_#[tableName]_columns"
 *         />
 *       </columns>
 *       <primaryKeys>
 *         <primaryKey 
 *             schema="#[schema]" 
 *             tableName="#[tableName]" 
 *             pkName="#[pkName]" 
 *             <!-- #[schema]_#[tableName]_primaryKeys 可以快速找到表下属的主键 -->
 *             IO_INPUT_EXPRESSION="#[schema]_#[tableName]_primaryKeys" 
 *             IO_OUTPUT_EXPRESSION="#[schema]_#[tableName]_primaryKeys"
 *         >
 *           <pkColumnNames>
 *             <pkColumnName 
 *                 <!-- #[schema]_#[tableName]_primaryKeys_#[pkName]_pkColumnNames 可以快速找到主键下属的字段 -->
 *                 IO_INPUT_EXPRESSION="#[schema]_#[tableName]_primaryKeys_#[pkName]_pkColumnNames" 
 *                 IO_OUTPUT_EXPRESSION="#[schema]_#[tableName]_primaryKeys_#[pkName]_pkColumnNames"
 *             >#[pkColumnName]</pkColumnName>
 *           </pkColumnNames>
 *         </primaryKey>
 *       </primaryKeys>
 *     </table>
 *   </tables>
 * </database>
 * }
 * 
 * {@link IOSimple#args 自定义参数集} 可通过 {@link PlaceholderCommons 占位符} 用作 key 的值引用
 * </pre>
 */
public class MemoryIOSource extends IOSource {

	/** inputKeyResult：[导入时] 存放 key 模板（如 #[schema]_#[tableName]_columns）与其构建后的动态 key 的映射关系 */
	private MultiValuedMap<String, String> inputKeyResult = new HashSetValuedHashMap<String, String>();
	/** inputResult：[导入时] 存放动态 key 与其下属数据的映射关系 */
	private ArrayListValuedHashMap<String, Map<String, Object>> inputResult = new ArrayListValuedHashMap<String, Map<String, Object>>();
	/** outputResult：[导出时] 存放动态 key 与其下属数据的映射关系 */
	private ArrayListValuedHashMap<String, Map<String, Object>> outputResult = new ArrayListValuedHashMap<String, Map<String, Object>>();

	/**
	 * <pre>
	 * 从中 {@link #inputKeyResult} 获取 key 模板对应的动态 key 集
	 * </pre>
	 * @param key key 模板
	 * @return 动态 key 集
	 */
	public List<String> getInputKeys(String key) {
		return new ArrayList<String>(this.inputKeyResult.get(key));
	}

	/**
	 * <pre>
	 * 从中 {@link #inputKeyResult} 获取所有动态 key 集
	 * </pre>
	 * @return 动态 key 集
	 */
	public List<String> getInputKeys() {
		return new ArrayList<String>(this.inputKeyResult.values());
	}

	/**
	 * <pre>
	 * 从中 {@link #inputKeyResult} 获取 key 模板对应的动态 key 集
	 * 获取所有动态 key 集的下属数据集
	 * </pre>
	 * @param key key 模板
	 * @return 所有动态 key 的下属数据集
	 */
	public List<Map<String, Object>> getInputResult(String key) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		List<String> inputKeys = getInputKeys(key);
		if (ValidateCommons.isNotEmpty(inputKeys)) {
			for (String inputKey : inputKeys) {
				List<Map<String, Object>> list = this.inputResult.get(inputKey);
				if (ValidateCommons.isNotEmpty(list)) {
					result.addAll(list);
				}
			}
		} else {
			List<Map<String, Object>> list = this.inputResult.get(key);
			if (ValidateCommons.isNotEmpty(list)) {
				result.addAll(list);
			}
		}
		return result;
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，获取其下属数据集
	 * </pre>
	 * @param key key 模板
	 * @param params 参数集
	 * @return 下属数据集
	 */
	public List<Map<String, Object>> getInputResult(String key, Map<String, Object> params) {
		if (ValidateCommons.isNotEmpty(params)) {
			key = PlaceholderCommons.replacePlaceholder(key, params);
		}
		return this.inputResult.get(key);
	}

	/**
	 * <pre>
	 * 清空 {@link inputKeyResult}、{@link inputResult} 数据
	 * </pre>
	 */
	public void clearInput() {
		this.inputKeyResult.clear();
		this.inputResult.clear();
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，并放入其下属数据
	 * </pre>
	 * @param key key 模板
	 * @param result 下属数据
	 */
	public void setOutputResult(String key, Map<String, Object> result) {
		setOutputResult(key, null, result);
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，并放入其下属数据
	 * </pre>
	 * @param key key 模板
	 * @param params 参数集
	 * @param result 下属数据
	 */
	public void setOutputResult(String key, Map<String, Object> params, Map<String, Object> result) {
		key = PlaceholderCommons.replacePlaceholder(key, new ParamMap<String, Object>().putAllParam(result).putAllParam(params));
		this.outputResult.put(key, result);
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，并放入其下属数据集
	 * </pre>
	 * @param key key 模板
	 * @param results 下属数据集
	 */
	public void setOutputResults(String key, List<Map<String, Object>> results) {
		setOutputResults(key, null, results);
	}

	/**
	 * <pre>
	 * 以参数集构建动态 key，并放入其下属数据集
	 * </pre>
	 * @param key key 模板
	 * @param params 参数集
	 * @param results 下属数据集
	 */
	public void setOutputResults(String key, Map<String, Object> params, List<Map<String, Object>> results) {
		if (ValidateCommons.isNotEmpty(results)) {
			for (Map<String, Object> result : results) {
				setOutputResult(key, params, result);
			}
		}
	}

	/**
	 * <pre>
	 * 清空 {@link outputResult} 数据
	 * </pre>
	 */
	public void clearOutput() {
		this.outputResult.clear();
	}

	@Override
	public void doInput(String key, Map<String, Object> param, boolean lastBatch, Map<String, Object> extraParams) {
		if (ValidateCommons.isNotEmpty(key) && ValidateCommons.isNotEmpty(param)) {
			autoReplace(param);
			String inputKey = PlaceholderCommons.replacePlaceholder(key, param);
			this.inputKeyResult.put(key, inputKey);
			this.inputResult.put(inputKey, param);
		}
	}

	@Override
	public void doOutput(String key, Map<String, Object> param, Callable<Map<String, Object>, Boolean> callable) throws Exception {
		if (ValidateCommons.isNotEmpty(key)) {
			key = PlaceholderCommons.replacePlaceholder(key, param);
			List<Map<String, Object>> results = this.outputResult.get(key);
			if (ValidateCommons.isNotEmpty(results)) {
				for (Map<String, Object> result : results) {
					result.putAll(param);
					autoReplace(result);
					callable.call(result);
				}
			}
		}
	}

}
